package com.fastTools.stream;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author zsh
 * @date 2020/7/27
 */
public class CollectionUtils {


    /**
     * list转换
     *
     * @param list 原始
     * @param converter 转换器
     * @param <T> 原始类型
     * @param <R> 转换后类型
     * @return 转换后的list
     */
    public static <T, R> List<R> convert(List<T> list, Function<T, R> converter) {
        if (list == null) {
            return null;
        }
        return stream(list).map(converter).collect(Collectors.toList());
    }

    /**
     * map中value的转换
     *
     * @param map 原始map
     * @param converter 转换器
     * @param <K> key
     * @param <T> 原始类型
     * @param <R> 转换后类型
     * @return 转换后的map
     */
    public static <K, T, R> Map<K, R> convert(Map<K, T> map, Function<T, R> converter) {
        if (map == null) {
            return null;
        }
        Map<K, R> result = new HashMap<>(map.size());
        map.forEach((k, t) -> result.put(k, converter.apply(t)));
        return result;
    }


    /**
     * sourceList 中是否包含一个 checkList中的一个元素
     *
     * @param sourceList 集合
     * @param checkList 检查的数组
     * @return true/false
     */
    public static <D> boolean hasOne(List<D> sourceList, List<D> checkList) {
        if (sourceList == null || sourceList.isEmpty() || checkList == null || checkList.isEmpty()) {
            return false;
        }
        for (D check : checkList) {
            if (sourceList.contains(check)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 从collection中查找某个属性值等于v的对象
     *
     * @param collection 集合
     * @param v 值 可以为null
     * @param function 获取属性的方法
     * @param <E> 对象
     * @param <V> 值
     * @return 查找到的对象
     */
    public static <E, V> E findOne(Collection<E> collection, V v, Function<E, V> function) {
        if (collection == null) {
            return null;
        }
        for (E e : collection) {
            if (v == null ? function.apply(e) == null : v.equals(function.apply(e))) {
                return e;
            }
        }
        return null;
    }

    /**
     * 从collection中查找某个属性值等于v的对象
     *
     * @param collection 集合
     * @param function 获取属性的方法
     * @param <E> 对象
     * @return 查找到的对象
     */
    public static <E> E findOne(Collection<E> collection, Predicate<E> function) {
        if (collection == null) {
            return null;
        }
        for (E e : collection) {
            if (function.test(e)) {
                return e;
            }
        }
        return null;
    }


    private static <T> Stream<T> stream(List<T> list) {
        return list.parallelStream();
    }


    /**
     * 收集对象连的某个属性值
     *
     * @param collection 集合
     * @param function 获取属性的方法
     * @param <T> 对象
     * @param <R> 返回的属性值
     * @return 属性的集合
     */
    public static <T, R> List<R> asList(Collection<T> collection, Function<T, R> function) {
        if (collection == null) {
            return null;
        }
        return collection.parallelStream().map(function).collect(Collectors.toList());
    }

    /**
     * 收集对象连的某个属性值
     *
     * @param collection 集合
     * @param function 获取属性的方法
     * @param <T> 对象
     * @param <R> 返回的属性值
     * @return 属性的集合
     */
    public static <T, R> Set<R> asSet(Collection<T> collection, Function<T, R> function) {
        if (collection == null) {
            return null;
        }
        return collection.parallelStream().map(function).collect(Collectors.toSet());
    }

    public static <R> List<R> filter(List<R> list, Predicate<R> predicate) {
        if (list == null) {
            return null;
        }
        return stream(list).filter(predicate).collect(Collectors.toList());
    }

    public static <T, R> List<R> filterAndConvert(List<T> list, Predicate<T> predicate, Function<T, R> function) {
        if (list == null) {
            return null;
        }
        return stream(list).filter(predicate).map(function).collect(Collectors.toList());
    }

    public static <T, R> List<R> toUniqueList(List<T> list, Function<T, R> function) {
        if (list == null) {
            return null;
        }
        return stream(list).map(function).distinct().collect(Collectors.toList());
    }

    public static <T> boolean anyMatch(List<T> list, Predicate<T> predicate) {
        if (list == null) {
            return false;
        }
        return stream(list).anyMatch(predicate);
    }

    public static <T> boolean allMatch(List<T> list, Predicate<T> predicate) {
        if (list == null) {
            return false;
        }
        return stream(list).allMatch(predicate);
    }


    public static <R> Set<R> filterSet(List<R> list, Predicate<R> predicate) {
        if (list == null) {
            return null;
        }
        return stream(list).filter(predicate).collect(Collectors.toSet());
    }

    public static <T, R> Set<R> filterSetAndConvert(List<T> list, Predicate<T> predicate, Function<T, R> function) {
        if (list == null) {
            return null;
        }
        return stream(list).filter(predicate).map(function).collect(Collectors.toSet());
    }

    public static <T, R> List<T> distinctByProperty(List<T> list, Function<T, R> propertyExtractor) {
        if (list == null) {
            return null;
        }
        Set<Object> seen = ConcurrentHashMap.newKeySet();
        return stream(list).filter(t -> seen.add(propertyExtractor.apply(t))).collect(Collectors.toList());
    }

    public static <T, R> Set<R> toSet(List<T> list, Function<T, R> function) {
        if (list == null) {
            return null;
        }
        return stream(list).map(function).collect(Collectors.toSet());
    }

    public static <T, K> Map<K, T> toMap(List<T> list, Function<T, K> keyMapper) {
        if (list == null) {
            return null;
        }
        return toMap(list, keyMapper, t -> t);
    }

    public static <T, K, V> Map<K, V> toMap(List<T> list, Function<T, K> keyMapper, Function<T, V> valueMapper) {
        if (list == null) {
            return null;
        }
        return toMap(list, keyMapper, valueMapper, (v, v2) -> v);
    }

    public static <T, K> Map<K, T> toMap(List<T> list, Function<T, K> keyMapper, BinaryOperator<T> mergeFunction) {
        if (list == null) {
            return null;
        }
        return toMap(list, keyMapper, t -> t, mergeFunction);
    }

    public static <T, K, V> Map<K, V> toMap(List<T> list, Function<T, K> keyMapper, Function<T, V> valueMapper, BinaryOperator<V> mergeFunction) {
        if (list == null) {
            return null;
        }
        return stream(list).collect(Collectors.toMap(keyMapper, valueMapper, mergeFunction));
    }

    public static <K, T> Map<K, List<T>> group(List<T> list, Function<T, K> function) {
        if (list == null) {
            return null;
        }
        return stream(list).collect(Collectors.groupingBy(function));
    }

    public static <K, T, R> Map<K, List<R>> groupAndConvert(List<T> list, Function<T, K> function, Function<T, R> convert) {
        if (list == null) {
            return null;
        }
        return stream(list).collect(Collectors.groupingBy(function, Collectors.mapping(convert, Collectors.toList())));
    }

    public static <K, T> Map<K, Set<T>> groupSet(List<T> list, Function<T, K> function) {
        if (list == null) {
            return null;
        }
        return stream(list).collect(Collectors.groupingBy(function, Collectors.toSet()));
    }

    public static <K, T, R> Map<K, Set<R>> groupSetAndConvert(List<T> list, Function<T, K> function, Function<T, R> convert) {
        if (list == null) {
            return null;
        }
        return stream(list).collect(Collectors.groupingBy(function, Collectors.mapping(convert, Collectors.toSet())));
    }
}
